/*
 * $Id: UserServiceImpl.java 33 2007-04-22 21:36:32Z aaron.day $
 */
package org.crazydays.core;


import java.util.Map;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.HibernateException;

import org.crazydays.hibernate.HibernateUtil;
import org.crazydays.core.data.PolisAcl;
import org.crazydays.core.data.PolisAclGroup;
import org.crazydays.core.data.PolisAclGroupKey;
import org.crazydays.core.data.PolisGroup;
import org.crazydays.core.data.PolisRight;
import org.crazydays.core.data.PolisUser;

/**
 * UserServiceImpl
 */
public class UserServiceImpl
	extends AbstractService
	implements IUserService
{
	/**
	 * Create a standard user. The user will have three basic acls created,
	 * a private (u+rw), a protected (u+rw,User g+r), and a public
	 * (u+rw,User g+r, Guest g+r).
	 * 
	 * @param user User
	 * @return User
	 * @throws DataException
	 */
	public PolisUser createStandardUser(PolisUser user)
		throws DataException
	{
		if (user == null) {
			throw new IllegalArgumentException("user == null");
		}

		Session session = null;
		Transaction transaction = null;

		try {
			session = HibernateUtil.getSession();
			transaction = session.beginTransaction();

			// load the read and write rights to be used by the acls
			PolisRight readRight = (PolisRight) HibernateUtil
					.loadPolisDataByName(session,
							PolisRight.class,
							PolisRight.NAME_READ);
			PolisRight writeRight = (PolisRight) HibernateUtil
					.loadPolisDataByName(session,
							PolisRight.class,
							PolisRight.NAME_WRITE);

			// create user
			user = saveUser(session, user);

			// user's private group
			PolisGroup userGroup = new PolisGroup();
			userGroup.setName(user.getName());
			userGroup.setDescription(user.getName() + " Group");
			userGroup.setUser(user);

			// add user to private group
			userGroup = addUser(session, userGroup, user);

			// create user group
			userGroup = saveGroup(session, userGroup);

			// load users group
			PolisGroup usersGroup = loadGroup(session,
					PolisGroup.NAME_USER);

			// add user to users group
			usersGroup = addUser(session, usersGroup, user);

			// load guest group
			PolisGroup guestGroup = loadGroup(session,
					PolisGroup.NAME_GUEST);

			// TODO: evaluate
			// acl group
			// PolisAclGroupKey aclGroupKey = null;
			// PolisAclGroup aclGroup = null;

			// user private acl
			PolisAcl privateAcl = new PolisAcl();
			privateAcl.setName(user.getName() + " Private");
			privateAcl.setDescription(user.getName()
					+ " Private ACL");
			privateAcl.setUser(user);

			// create private acl
			privateAcl = saveAcl(session, privateAcl);

			// add private acl rights
			privateAcl = addRight(session, privateAcl, userGroup,
					readRight);
			privateAcl = addRight(session, privateAcl, userGroup,
					writeRight);

			// user protected acl
			PolisAcl protectedAcl = new PolisAcl();
			protectedAcl.setName(user.getName() + " Protected");
			protectedAcl.setDescription(user.getName()
					+ " Protected ACL");
			protectedAcl.setUser(user);

			// create protected acl
			protectedAcl = saveAcl(session, protectedAcl);

			// add protected acl rights
			protectedAcl = addRight(session, protectedAcl,
					userGroup, readRight);
			protectedAcl = addRight(session, protectedAcl,
					userGroup, writeRight);
			protectedAcl = addRight(session, protectedAcl,
					usersGroup, readRight);

			// user public acl
			PolisAcl publicAcl = new PolisAcl();
			publicAcl.setName(user.getName() + " Public");
			publicAcl
					.setDescription(user.getName()
							+ " Public ACL");
			publicAcl.setUser(user);

			// create public acl
			publicAcl = saveAcl(session, publicAcl);

			// add public acl rights
			publicAcl = addRight(session, publicAcl, userGroup,
					readRight);
			publicAcl = addRight(session, publicAcl, userGroup,
					writeRight);
			publicAcl = addRight(session, publicAcl, usersGroup,
					readRight);
			publicAcl = addRight(session, publicAcl, guestGroup,
					readRight);

			// commit transaction
			transaction.commit();

			return user;
		} catch (HibernateException e) {
			LOG.error("HibernateException: " + e.getMessage(), e);

			// rollback transaction because of error
			if (transaction != null) {
				transaction.rollback();
			}

			throw new DataException("Unable to create User", e,
					user);
		} finally {
			session.close();
		}
	}

	/**
	 * Load user by name.
	 * 
	 * @param session Session
	 * @param name Name
	 * @return User
	 * @throws DataException
	 */
	public PolisUser loadUser(Session session, String name)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (name == null) {
			throw new IllegalArgumentException("name == null");
		}

		try {
			PolisUser user = (PolisUser) HibernateUtil
					.loadPolisDataByName(session,
							PolisUser.class, name);

			LOG.debug("Loaded: " + user);

			return user;
		} catch (HibernateException e) {
			LOG.error("HibernateException: " + e.getMessage(), e);
			throw new DataException("Unable to load User: " + name,
					e);
		}
	}

	/**
	 * Save user.
	 * 
	 * @param session Session
	 * @param user User
	 * @return User
	 * @throws DataException
	 */
	public PolisUser saveUser(Session session, PolisUser user)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (user == null) {
			throw new IllegalArgumentException("user == null");
		}

		try {
			HibernateUtil.savePolisData(session, user);

			LOG.debug("Saved: " + user);

			return user;
		} catch (HibernateException e) {
			LOG.error("HibernateExcetpion: " + e.getMessage(), e);
			throw new DataException("Unable to save User", e, user);
		}
	}

	/**
	 * Delete user.
	 * 
	 * @param session Session
	 * @param user User
	 * @return User
	 * @see IUserService#deleteUser(Session,PolisUser)
	 */
	public PolisUser deleteUser(Session session, PolisUser user)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (user == null) {
			throw new IllegalArgumentException("user == null");
		}

		try {
			HibernateUtil.deletePolisData(session, user);

			LOG.debug("Deleted: " + user);

			return user;
		} catch (HibernateException e) {
			LOG.error("HibernateExcetpion: " + e.getMessage(), e);
			throw new DataException("Unable to delete User", e,
					user);
		}
	}

	/**
	 * Load group by name.
	 * 
	 * @param session Session
	 * @param name Name
	 * @return Group
	 * @throws DataException
	 */
	public PolisGroup loadGroup(Session session, String name)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (name == null) {
			throw new IllegalArgumentException("name == null");
		}

		try {
			PolisGroup group = (PolisGroup) HibernateUtil
					.loadPolisDataByName(session,
							PolisGroup.class, name);

			LOG.debug("Loaded: " + group);

			return group;
		} catch (HibernateException e) {
			LOG.error("HibernateException: " + e.getMessage(), e);
			throw new DataException(
					"Unable to load Group: " + name, e);
		}
	}

	/**
	 * Save group.
	 * 
	 * @param session Session
	 * @param group Group
	 * @return Group
	 * @throws DataException
	 */
	public PolisGroup saveGroup(Session session, PolisGroup group)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (group == null) {
			throw new IllegalArgumentException("group == null");
		}

		try {
			HibernateUtil.savePolisData(session, group);

			LOG.debug("Saved: " + group);

			return group;
		} catch (HibernateException e) {
			LOG.error("HibernateExcetpion: " + e.getMessage(), e);
			throw new DataException("Unable to save Group", e,
					group);
		}
	}

	/**
	 * Delete group.
	 * 
	 * @param session Session
	 * @param group Group
	 * @return Group
	 * @throws DataException
	 * @see IUserService#deleteGroup(Session,PolisGroup)
	 */
	public PolisGroup deleteGroup(Session session, PolisGroup group)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (group == null) {
			throw new IllegalArgumentException("group == null");
		}

		try {
			HibernateUtil.deletePolisData(session, group);

			LOG.debug("Deleted: " + group);

			return group;
		} catch (HibernateException e) {
			LOG.error("HibernateExcetpion: " + e.getMessage(), e);
			throw new DataException("Unable to delete Group", e,
					group);
		}
	}

	/**
	 * Add user to group.
	 * 
	 * @param session Session
	 * @param group Group
	 * @param user User
	 * @return Group
	 * @throws DataException
	 */
	public PolisGroup addUser(Session session, PolisGroup group,
			PolisUser user)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (group == null) {
			throw new IllegalArgumentException("group == null");
		}
		if (user == null) {
			throw new IllegalArgumentException("user == null");
		}

		Set<PolisUser> users = group.getUsers();
		if (users == null) {
			throw new IllegalStateException("group.users == null");
		}

		// add user
		users.add(user);

		try {
			HibernateUtil.savePolisData(session, group);
		} catch (HibernateException e) {
			LOG.error("HibernateException: " + e.getMessage(), e);
			throw new DataException("Unable to save PolisGroup", e,
					group);
		}

		return group;
	}

	/**
	 * Load acl.
	 * 
	 * @param session Session
	 * @param acl Acl
	 * @return Acl
	 * @throws DataException
	 * @see IUserService#loadAcl(Session,String)
	 */
	public PolisAcl loadAcl(Session session, String name)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (name == null) {
			throw new IllegalArgumentException("name == null");
		}

		try {
			PolisAcl acl = (PolisAcl) HibernateUtil
					.loadPolisDataByName(session,
							PolisAcl.class, name);

			LOG.debug("Loaded: " + acl);

			return acl;
		} catch (HibernateException e) {
			LOG.error("HibernateException: " + e.getMessage(), e);
			throw new DataException("Unable to load Acl: " + name,
					e);
		}
	}

	/**
	 * Save acl.
	 * 
	 * @param session Session
	 * @param acl Acl
	 * @return Acl
	 * @throws DataException
	 * @see IUserService#saveAcl(Session,PolisAcl)
	 */
	public PolisAcl saveAcl(Session session, PolisAcl acl)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (acl == null) {
			throw new IllegalArgumentException("acl == null");
		}

		try {
			HibernateUtil.savePolisData(session, acl);
		} catch (HibernateException e) {
			LOG.error("HibernateException: " + e.getMessage(), e);
			throw new DataException("Unable to save PolisAcl", e,
					acl);
		}

		return acl;
	}

	/**
	 * Delete acl.
	 * 
	 * @param session Session
	 * @param acl Acl
	 * @return Acl
	 * @throws DataException
	 * @see IUserService#deleteAcl(Session,PolisAcl)
	 */
	public PolisAcl deleteAcl(Session session, PolisAcl acl)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (acl == null) {
			throw new IllegalArgumentException("acl == null");
		}

		try {
			HibernateUtil.deletePolisData(session, acl);

			LOG.debug("Deleted: " + acl);

			return acl;
		} catch (HibernateException e) {
			LOG.error("HibernateExcetpion: " + e.getMessage(), e);
			throw new DataException("Unable to delete acl", e, acl);
		}

	}

	/**
	 * Add right to an acl. DataException being thrown causes the
	 * transaction to be rolled back.
	 * 
	 * @param session Session
	 * @param acl Acl
	 * @param group Group
	 * @param right Right
	 * @return Acl
	 * @throws DataException
	 */
	public PolisAcl addRight(Session session, PolisAcl acl,
			PolisGroup group, PolisRight right)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (acl == null) {
			throw new IllegalArgumentException("acl == null");
		}
		if (group == null) {
			throw new IllegalArgumentException("group == null");
		}
		if (right == null) {
			throw new IllegalArgumentException("right == null");
		}

		// key
		PolisAclGroupKey aclGroupKey = new PolisAclGroupKey();
		aclGroupKey.setAclId(acl.getId());
		aclGroupKey.setGroupId(group.getId());
		aclGroupKey.setRightId(right.getId());

		// acl group
		PolisAclGroup aclGroup = new PolisAclGroup();
		aclGroup.setKey(aclGroupKey);
		aclGroup.setAllow(true);

		// add group
		acl.addAclGroup(aclGroup);

		try {
			HibernateUtil.savePolisData(session, acl);
		} catch (HibernateException e) {
			LOG.error("HibernateException: " + e.getMessage(), e);
			throw new DataException("Unable to save PolisAcl", e,
					acl);
		}

		return acl;
	}

	/**
	 * Delete right from acl.
	 * 
	 * @param session Session
	 * @param acl Acl
	 * @param group Group
	 * @param right Right
	 * @return Acl
	 * @throws DataException
	 */
	public PolisAcl deleteRight(Session session, PolisAcl acl,
			PolisGroup group, PolisRight right)
		throws DataException
	{
		if (session == null) {
			throw new IllegalArgumentException("session == null");
		}
		if (acl == null) {
			throw new IllegalArgumentException("acl == null");
		}
		if (group == null) {
			throw new IllegalArgumentException("group == null");
		}
		if (right == null) {
			throw new IllegalArgumentException("right == null");
		}

		// create acl group key
		PolisAclGroupKey aclGroupKey = new PolisAclGroupKey();
		aclGroupKey.setAclId(acl.getId());
		aclGroupKey.setGroupId(group.getId());
		aclGroupKey.setRightId(right.getId());

		Map<PolisAclGroupKey, PolisAclGroup> aclGroups = acl
				.getAclGroups();
		if (aclGroups == null) {
			throw new DataException("acl.aclGroups == null", acl);
		}

		if (aclGroups.keySet().contains(aclGroupKey)) {
			// remove acl group
			aclGroups.remove(aclGroupKey);

			try {
				HibernateUtil.savePolisData(session, acl);
			} catch (HibernateException e) {
				LOG.error("Unable to remove aclGroup from acl",
						e);
				throw new DataException(
						"Unable to remove aclGroup from acl",
						e, acl);
			}
		} else {
			LOG.warn("Acl does not contain aclGroup");
		}

		return acl;
	}
}
